package com.xbn.netty.simpleTcp;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.util.concurrent.TimeUnit;

/**
 * @author XBN
 * @date 2020/10/19 13:33
 * @email 78742541@qq.com
 */
/*
* 自定义一个handler，需要继承Netty规定的某个handler适配器ChannelInboundHandlerAdapter就是其中一个
* 只有继承了适配器，我们自定义的handler才可以有相关的功能才能称为handler
*
* */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    //读取事件的事件和消息
    /*
    * ChannelHandlerContext：上下文对象，包含了管道pipeline,通道channel，地址
    * Object msg：就是客户端发送的数据，默认Object
    * */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        /*————————————————用户程序自定义普通任务----------------------------*/
        //异步执行，提交到该channe对应的NIOEventLoop的taskQueue中
        ctx.channel().eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                //我们假设这里有一些业务逻辑需要运行很长一段时间，用休眠来模拟,服务端阻塞，那么客户端就需要等待10s才会得到响应
                try {
                    Thread.sleep(10*1000);
                } catch (InterruptedException e) {
                    System.out.println("业务发生异常");
                    e.printStackTrace();
                }
            }
        });
   /*往任务队列中加入第二个任务，这个任务要等过20s（上一个任务10s，这个任务10s）才运行完，
   虽然代码中新开了一个线程，但是实际上他们用的是同一个线程要先等前面的任务先运行完*/
        ctx.channel().eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                //我们假设这里有一些业务逻辑需要运行很长一段时间，用休眠来模拟,服务端阻塞，那么客户端就需要等待10s才会得到响应
                try {
                    Thread.sleep(10*1000);
                } catch (InterruptedException e) {
                    System.out.println("业务发生异常");
                    e.printStackTrace();
                }
            }
        });
        /*————————————————用户程序自定义普通任务结束----------------------------*/
        /*———————用户自定义定时任务，该任务提交到scheduleTaskQueue中---------------*/
       //可以延时执行，延时5s
        ctx.channel().eventLoop().schedule(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10*1000);
                } catch (InterruptedException e) {
                    System.out.println("业务发生异常");
                    e.printStackTrace();
                }
            }
        },5, TimeUnit.SECONDS);

        System.out.println("server ctx="+ctx);
        //将msg转成一个ByteBuf,注意着不是Nio包中的ByteBuffer，ByteBuf拥有更高的性能
        ByteBuf buf= (ByteBuf) msg;
        System.out.println("客户端发送的消息是;"+buf.toString(CharsetUtil.UTF_8));
        System.out.println("客户端地址:"+ctx.channel().remoteAddress());
    }
        //数据读取完毕（也就是channelRead方法完毕），之后的操作通过这个方法
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //把数据写到缓冲，同时刷新，刷新指的是将缓冲区的数据打到通道中
        //只有将数据打入到通道中，才能将数据发送给客户端
        //writeAndFlush其实就是write方法和flush方法的合并
        //先对他们进行编码然后再进行发送
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端，本条消息是服务器给你的响应"
                ,CharsetUtil.UTF_8)
        );
    }

    //处理异常也有专门的方法
//一般来说，如果发生了异常，我们就需要关闭通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}
